/*----------------------------------------------------------------------------
 * Tencent is pleased to support the open source community by making TencentOS
 * available.
 *
 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
 * If you have downloaded a copy of the TencentOS binary from Tencent, please
 * note that the TencentOS binary is licensed under the BSD 3-Clause License.
 *
 * If you have downloaded a copy of the TencentOS source code from Tencent,
 * please note that TencentOS source code is licensed under the BSD 3-Clause
 * License, except for the third-party components listed below which are
 * subject to different license terms. Your integration of TencentOS into your
 * own projects may require compliance with the BSD 3-Clause License, as well
 * as the other licenses applicable to the third-party components included
 * within TencentOS.
 *---------------------------------------------------------------------------*/

#include "tos_k.h"

__API__ k_err_t tos_knl_init(void)
{
	k_err_t err;

	cpu_init();

	readyqueue_init();

#if TOS_CFG_MMHEAP_EN > 0
#if TOS_CFG_MMHEAP_DEFAULT_POOL_EN > 0u
	err = mmheap_init_with_pool(k_mmheap_default_pool,
	TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE);
#else
	err = mmheap_init();
#endif
	if (err != K_ERR_NONE)
	{
		return err;
	}
#endif

	err = knl_idle_init();
	if (err != K_ERR_NONE)
	{
		return err;
	}

#if TOS_CFG_TIMER_EN > 0
	err = timer_init();
	if (err != K_ERR_NONE)
	{
		return err;
	}
#endif

#if TOS_CFG_PWR_MGR_EN > 0U
	pm_init();
#endif

#if TOS_CFG_TICKLESS_EN > 0u
	tickless_init();
#endif

	return K_ERR_NONE;
}

__API__ void tos_knl_irq_enter(void)
{
	if (!tos_knl_is_running())
	{
		return;
	}

	if (unlikely(k_irq_nest_cnt >= K_NESTING_LIMIT_IRQ))
	{
		return;
	}

	++k_irq_nest_cnt;
}

__API__ void tos_knl_irq_leave(void)
{
	TOS_CPU_CPSR_ALLOC();

	if (!tos_knl_is_running())
	{
		return;
	}

	TOS_CPU_INT_DISABLE()
	;
	if (!knl_is_inirq())
	{
		TOS_CPU_INT_ENABLE()
		;
		return;
	}

	--k_irq_nest_cnt;

	if (knl_is_inirq())
	{
		TOS_CPU_INT_ENABLE()
		;
		return;
	}

	if (knl_is_sched_locked())
	{
		TOS_CPU_INT_ENABLE()
		;
		return;
	}

	k_next_task = readyqueue_highest_ready_task_get();
	if (knl_is_self(k_next_task))
	{
		TOS_CPU_INT_ENABLE()
		;
		return;
	}

	cpu_irq_context_switch();
	TOS_CPU_INT_ENABLE()
	;
}

__API__ k_err_t tos_knl_sched_lock(void)
{
	TOS_CPU_CPSR_ALLOC();

	TOS_IN_IRQ_CHECK();

	if (!tos_knl_is_running())
	{
		return K_ERR_KNL_NOT_RUNNING;
	}

	if (k_sched_lock_nest_cnt >= K_NESTING_LIMIT_SCHED_LOCK)
	{
		return K_ERR_LOCK_NESTING_OVERFLOW;
	}

	TOS_CPU_INT_DISABLE()
	;
	++k_sched_lock_nest_cnt;
	TOS_CPU_INT_ENABLE()
	;
	return K_ERR_NONE;
}

__API__ k_err_t tos_knl_sched_unlock(void)
{
	TOS_CPU_CPSR_ALLOC();

	TOS_IN_IRQ_CHECK();

	if (!tos_knl_is_running())
	{
		return K_ERR_KNL_NOT_RUNNING;
	}

	if (!knl_is_sched_locked())
	{
		return K_ERR_SCHED_NOT_LOCKED;
	}

	TOS_CPU_INT_DISABLE()
	;
	--k_sched_lock_nest_cnt;
	TOS_CPU_INT_ENABLE()
	;

	knl_sched();
	return K_ERR_NONE;
}

__API__ k_err_t tos_knl_start(void)
{
	if (unlikely(tos_knl_is_running()))
	{
		return K_ERR_KNL_RUNNING;
	}

	k_next_task = readyqueue_highest_ready_task_get();
	k_curr_task = k_next_task;
	k_knl_state = KNL_STATE_RUNNING;

//    printf("start\r\n");
	cpu_sched_start();

	return K_ERR_NONE;
}

__API__ int tos_knl_is_running(void)
{
	return k_knl_state == KNL_STATE_RUNNING;
}

#if TOS_CFG_TICKLESS_EN > 0u

/**
 * @brief Get the remain ticks of the first oncoming task.
 *
 * @return The remian ticks of the first oncoming task to be scheduled.
 */
__KNL__ k_tick_t knl_next_expires_get(void)
{
	k_tick_t tick_next_expires;
#if TOS_CFG_TIMER_EN > 0u
	k_tick_t timer_next_expires;
#endif

	tick_next_expires = tick_next_expires_get();

#if TOS_CFG_TIMER_EN > 0u
	timer_next_expires = timer_next_expires_get();
#endif

#if TOS_CFG_TIMER_EN > 0u
	return tick_next_expires < timer_next_expires ? tick_next_expires : timer_next_expires;
#else
	return tick_next_expires;
#endif
}

#endif

__KNL__ void knl_sched(void)
{
	TOS_CPU_CPSR_ALLOC();

	if (unlikely(!tos_knl_is_running()))
	{
		return;
	}

	if (knl_is_inirq())
	{
		return;
	}

	if (knl_is_sched_locked())
	{
		return;
	}

	TOS_CPU_INT_DISABLE()
	;
	k_next_task = readyqueue_highest_ready_task_get();
	if (knl_is_self(k_next_task))
	{
		TOS_CPU_INT_ENABLE()
		;
		return;
	}

	cpu_context_switch();
	TOS_CPU_INT_ENABLE()
	;
}

__KNL__ int knl_is_sched_locked(void)
{
	return k_sched_lock_nest_cnt > 0u;
}

__KNL__ int knl_is_inirq(void)
{
	return k_irq_nest_cnt > 0u;
}

__KNL__ int knl_is_idle(k_task_t *task)
{
	return task == &k_idle_task;
}

__KNL__ int knl_is_self(k_task_t *task)
{
	return task == k_curr_task;
}

__STATIC__ void knl_idle_entry(void *arg)
{
	arg = arg; // make compiler happy

	while (K_TRUE)
	{
#if TOS_CFG_TASK_DYNAMIC_CREATE_EN > 0u
		task_free_all();
#endif

#if TOS_CFG_PWR_MGR_EN > 0u
		pm_power_manager();
#endif
	}
}

__KNL__ k_err_t knl_idle_init(void)
{
	return tos_task_create(&k_idle_task, "idle", knl_idle_entry, K_NULL,
	K_TASK_PRIO_IDLE, k_idle_task_stk_addr, k_idle_task_stk_size, 0);
}

